package com.gitee.fastmybatis.core.util;

import com.gitee.fastmybatis.core.ext.code.util.ReflectUtil;

import java.lang.reflect.Field;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.RandomAccess;

/**
 * List工具类
 *
 * @author thc
 */
public class ListUtil {

    /**
     * 将list分成几个子list，每个子list存放size个元素
     *
     * @param list 集合
     * @param size 每个子list存放元素个数
     * @param <T>  类型
     * @return 返回子list集合
     */
    public static <T> List<List<T>> partition(List<T> list, int size) {
        Objects.requireNonNull(list);
        if (size < 1) {
            throw new IllegalArgumentException("size must > 0");
        }
        return (list instanceof RandomAccess)
                ? new RandomAccessPartition<>(list, size)
                : new Partition<>(list, size);
    }

    /**
     * 返回不为null的实体类字段名
     *
     * @param list 集合
     * @param <T>
     * @return 返回不为null的实体类字段名
     */
    public static <T> List<String> getNotNullFieldNames(Collection<T> list) {
        if (list == null || list.isEmpty()) {
            throw new IllegalArgumentException("argument 'list' can not empty");
        }
        // 取第一个
        T obj = list.iterator().next();
        Class<?> entityClass = obj.getClass();
        List<Field> fields = ReflectUtil.getDeclaredFields(entityClass);
        List<String> notNullFieldNames = new ArrayList<>(fields.size());
        for (Field field : fields) {
            field.setAccessible(true);
            Object value = ReflectUtil.getField(field, obj);
            if (value != null) {
                notNullFieldNames.add(field.getName());
            }
        }
        return notNullFieldNames;
    }

    private static class Partition<T> extends AbstractList<List<T>> {
        final List<T> list;
        final int size;

        Partition(List<T> list, int size) {
            this.list = list;
            this.size = size;
        }

        @Override
        public List<T> get(int index) {
            checkElementIndex(index, size());
            int start = index * size;
            int end = Math.min(start + size, list.size());
            return list.subList(start, end);
        }

        @Override
        public int size() {
            return (list.size() + size - 1) / size;
        }

        @Override
        public boolean isEmpty() {
            return list.isEmpty();
        }
    }

    private static class RandomAccessPartition<T> extends Partition<T> implements RandomAccess {
        RandomAccessPartition(List<T> list, int size) {
            super(list, size);
        }
    }


    private static int checkElementIndex(int index, int size) {
        // Carefully optimized for execution by hotspot (explanatory comment above)
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("index error, index:" + index + ", size:" + size);
        }
        return index;
    }

}
